
/*****************************************************************************\
 *                             Loader.cpp
\*****************************************************************************/

/*! @file 
 *
 *  @brief
 *
 *  @author Weiyu Yi, inweiyu@googlemail.com
 */

//---------------------------------------------------------------------------
//  Includes
//---------------------------------------------------------------------------

#include <limits>
#include <string>
#include <iostream>
#include <fstream>

#include <defEException.h>

#include <tinyxml.h>

#include "Loader.h"

namespace vhand
{

//***************************************************************************
//                         static help functions
//***************************************************************************

/** 
 * 
 * @param vec4
 * @param str
 */
void Loader::string2vec( Vector4 *vec4, std::string str )
{
    std::string linepart;
    for ( unsigned int i=0; i<4; i++ )
    {
        findNextPartString( &str, &linepart, " " );
        if ( linepart == "" )
        {
            throw XError( "XErrors while calling %s : %s:\n\tThe string is less than 4-elements vector.\n",
                          __FILE__, __FUNCTION__ );
        }
        (*vec4)[i] = atof( linepart.c_str() );
    }

    findNextPartString( &str, &linepart, " " );
    if ( linepart != "" )
    {
        throw XError( "XErrors while calling %s : %s:\n\tThe string is greater than 4-elements vector.\n",
                __FILE__, __FUNCTION__ );
    }
    return;
}

/** 
 * 
 * @param vec3
 * @param str
 */
void Loader::string2vec( Vector3 *vec3, std::string str )
{
    std::string linepart;
    for ( unsigned int i=0; i<3; i++ )
    {
        findNextPartString( &str, &linepart, " " );
        if ( linepart == "" )
        {
            throw XError( "XErrors while calling %s : %s:\n\tThe string is less than 4-elements vector.\n",
                          __FILE__, __FUNCTION__ );
        }
        (*vec3)[i] = atof( linepart.c_str() );
    }

    findNextPartString( &str, &linepart, " " );
    if ( linepart != "" )
    {
        throw XError( "XErrors while calling %s : %s:\n\tThe string is greater than 4-elements vector.\n",
                __FILE__, __FUNCTION__ );
    }
    return;
}


/** Find the substring from input string with a given delimiter
 * 
 * @param line  input string, after the function, whose content will be the
 *              left part of the input string
 * @param part  the found substring
 * @param delim delimiter
 *
 *      line(before) = part + delim + line(after);
 */
void Loader::findNextPartString( std::string *line,
                                 std::string *part,
                                 const std::string &delim )
{
    std::size_t found = line->find_first_of( delim );
    *part = line->substr( 0, found );

    if ( found == std::string::npos )
    {
        *line = "";

    } else
    {
        *line = line->substr( found+1 );
    }
}


/** Overloaded
 * 
 * @param line
 * @param part
 * @param delim
 */
void Loader::findNextPartString( std::string *line,
                                 std::string *part,
                                 const char *delim )
{
    std::string delim_str = std::string( delim );
    findNextPartString( line, part, delim_str );
}


//***************************************************************************
//                             Loader
//***************************************************************************

Loader::Loader( const std::string &cfg )
: 
    _fileloaddir( FILE_LOAD_PATH )
{ 
    std::string filefullname = _fileloaddir + cfg;
    std::string line;
    std::string linepart;
    std::ifstream ifile( filefullname.c_str() );
    if ( ! ifile.is_open() )
    {
        throw XError( "Errors while calling %s : %s :\n\tCannot open file %s.\n",
                      __FILE__, __FUNCTION__,
                      filefullname.c_str() );
    }

    while( ! ifile.eof() )
    {
        getline( ifile, line );
        if ( line[0] == '#' )
            // comment
            continue;

        findNextPartString( &line, &linepart, "=" );
        _config[ linepart ] = line;
    }
}


void Loader::loadHand( Hand *hand )
{
    // load bones first
    // 1) bones' armature
    std::string armaturefile = _fileloaddir +  _config[ "skeleton" ];
    loadBonesArmature( hand->editArmature(), armaturefile );

    // 2) bones' geometry
    std::string bonesgeomfile = _fileloaddir + _config[ "bone-mesh" ];
    loadBonesGeom( hand->editBonesGeom(),
                   hand->editArmature(),
                   bonesgeomfile );

    // load skin
    // 3 ) skin's geometry
    std::string skingeomfile = _fileloaddir + _config[ "skin-mesh" ];
    loadSkinGeom( hand->editSkinGeom(),
                  skingeomfile );

    // load spheres
    // 4 ) spheres' location and radius
    std::string spheresfile = _fileloaddir + _config[ "spheres" ];
    loadSpheres( hand->editSphereFilledBody(), spheresfile );

    // 5 ) volume/mass-spring topology
    hand->editSphereFilledBody()->setHandGeometry( hand->editBonesGeom(),
                                                   hand->editSkinGeom() );
    // 5.1) test if such file exits
    spheresfile = _config[ "spheres" ];
    std::string spheresnum;
    findNextPartString( &spheresfile, &spheresnum, "." );
    findNextPartString( &spheresfile, &spheresnum, "." );
    std::string topofile = _fileloaddir + "Hand." + spheresnum + ".topology";
    // 5.2) load this file
    if ( 0 != loadTopology( hand->editSphereFilledBody()->editSphereTopology(), 
                            topofile ) )
    {
        // 5.3) if it doesn't exist, create the topology and then write it to file
        hand->editSphereFilledBody()->editSphereTopology()->initHost( hand->getBonesGeom(),
                                                                      hand->getSkinGeom(),
                                                                      hand->getSphereFilledBody()->getAllSphere(),
                                                                      hand->getSphereFilledBody()->getShortestRadius() );
        // and write it down
        writeTopology( hand->getSphereFilledBody()->getSphereTopology(), 
                       topofile );
    }

    // create volume
    // 6 ) create mass-spring system
    // 6.1) set mss-relative parameters
    loadSssArgument( hand->editSphereFilledBody() );
    // 6.2) create SphereFilledBody in host memory
    hand->editSphereFilledBody()->initHost();

    // 7) map spheres and skin vertices to the corresponding limb
    hand->initHost();

    // Till here, all initialization about hand in host memory has totally
    // been completed
    return;
}


/** 
 * 
 * @param armature
 * @param filename
 */
void Loader::loadBonesArmature( Armature *armature,
                                const std::string &filefullname )
{
    printf( "%s\n", filefullname.c_str() );
    TiXmlDocument doc( filefullname );
    if( ! doc.LoadFile() )
    {
        throw XError( "Errors while calling %s : %s :\n\tCannot open file %s.\n",
                      __FILE__, __FUNCTION__, filefullname.c_str() );
    }

    // doc open now
    TiXmlElement *root = doc.RootElement();
    int num_bones = 0;
    root->QueryIntAttribute( "NUMBONES", &num_bones );

    armature->_num_limbs = num_bones;
    armature->_alllimbrefs.resize( num_bones );

    TiXmlElement* bone = root->FirstChildElement( "BONE" );
    while ( bone != NULL )
    {
        printf( "one limb will be read---------------------------------------\n" );

        int id = 0;
        std::string name;
        bone->QueryIntAttribute( "ID", &id );
        bone->QueryStringAttribute( "NAME", &name );

        TiXmlElement* rotelem = bone->FirstChildElement( "ROTATION" );
        TiXmlElement* trlelem = bone->FirstChildElement( "TRANSLATION" );
        Vector4 rotvec;
        Vector3 trlvec;
        string2vec( &rotvec, rotelem->GetText() );
        string2vec( &trlvec, trlelem->GetText() );

        TiXmlElement* parent = bone->FirstChildElement( "PARENTID" );
        int pa_id = atoi( parent->GetText() );

        LimbRef limb;
        limb.setID( id );
        limb.setName( name );
        limb.setParentID( pa_id );
        limb.setTranslation( trlvec );

        /**
         * @care.
         *      The quaternion must be invert first, 
         */
        Quat rotquat( rotvec );

        Matrix4 rotmat = Matrix4::rotation( rotquat );
        Matrix4 rotmatinverse = defE::inverse( rotmat );
        limb.setRotation( rotmatinverse );
        // or
        //Quat rotquatinverse = inverse( rotquat );
        //limb.setRotation( rotquatinverse );


        printf( "Limb ID: %d\nLimb Name: %s\nLimb Quat(%f,%f,%f,%f)\nLimb Tran(%f,%f,%f)\n",
                id, name.c_str(),
                rotvec[0], rotvec[1], rotvec[2], rotvec[3],
                trlvec[0], trlvec[1], trlvec[2], trlvec[3] );

        TiXmlElement* child = bone->FirstChildElement( "CHILDID" );
        while ( child != NULL )
        {
            int ch_id = atoi( child->GetText() );
            limb.addChildID( ch_id );
            child = child->NextSiblingElement( "CHILDID" );
        }

        armature->_alllimbrefs[ id ] = limb;

        armature->_limb_str2id[ limb.getName() ] = limb.getID();
        armature->_limb_id2str[ limb.getID() ] = limb.getName();

        bone = bone->NextSiblingElement( "BONE" );
    }
}


/** 
 * 
 * @param bonesgeom
 * @param armature
 * @param filename
 *
 * @pre.cond.
 *      armature should be initialized before.
 * @see
 *      loadBonesArmature
 */
void Loader::loadBonesGeom( Geometry *bonesgeom,
                            Armature *armature,
                            const std::string &filefullname )
{
    bonesgeom->setFillMode( GL_FILL );

    ////////////////////////////////////////////////////////////////////////////
    // Assumed that the mesh is make of only triangles
    //
    bonesgeom->editAllPrimitiveSets()->resize( 1 );
    bonesgeom->editAllPrimitiveSets()->front().setType( GL_TRIANGLES );

    int num_limbs = armature->_num_limbs;
    std::vector< int > limboffsets( num_limbs );
    std::vector< int > limbsizes  ( num_limbs );

    std::ifstream ifile( filefullname.c_str() );
    if ( ! ifile.is_open() )
    {
        throw XError( "Errors while calling %s : %s :\n\tCannot open file %s.\n",
                      __FILE__, __FUNCTION__,
                      filefullname.c_str() );
    }

    ////////////////////////////////////////////////////////////////////////////
    // Assign the geometry while reading the obj file
    //
    std::string line;
    std::string linepart;
    unsigned int pntoffset = 0;
    unsigned int lastpntoffset = 0;
    int lastlimbid = -1;

    std::vector<Vector3> tmpnormals;
    std::vector<int> pos2nml;
    std::vector<int> pos2tex;

    while( ! ifile.eof() )
    {
        getline( ifile, line );
        findNextPartString( &line, &linepart, " " );

        // material
        if ( linepart == "mtllib" )
        {
            findNextPartString( &line, &linepart, " " );
            loadMaterial( bonesgeom, linepart );
        }

        // name of vertex group( limb )
        if( linepart == "o" )
        {
            findNextPartString( &line, &linepart, " " );
            int index = armature->findLimbID( linepart );
            limboffsets[ index ] = pntoffset;

            if ( lastlimbid != -1 )
            {
                limbsizes[ lastlimbid ] = pntoffset - lastpntoffset;
                lastpntoffset = pntoffset;
            }
            lastlimbid = index;
        }

        // position of vertex
        if( linepart == "v" )
        {
            Point3 pnt3;
            findNextPartString( &line, &linepart, " " );
            pnt3[0] = atof( linepart.c_str() );
            findNextPartString( &line, &linepart, " " );
            pnt3[1] = atof( linepart.c_str() );
            findNextPartString( &line, &linepart, " " );
            pnt3[2] = atof( linepart.c_str() );
            
            if ( line != "" )
            {
                throw XError( "Errors while calling %s : %s :\n\tThe vertex info in OBJ file has more than 3 elements.\n",
                               __FILE__, __FUNCTION__ );
            }

            // initialize the bones2limb map object
            (*armature->editBonesToLimb())[pntoffset] = lastlimbid;

            // add the new vertex into bones' geometry
            bonesgeom->editPositions()->editArray()->push_back( pnt3 );
            pntoffset += 1;
        }

        // normal of vertex
        if( linepart == "vn" )
        {
            Vector3 vec3;
            findNextPartString( &line, &linepart, " " );
            vec3[0] = atof( linepart.c_str() );
            findNextPartString( &line, &linepart, " " );
            vec3[1] = atof( linepart.c_str() );
            findNextPartString( &line, &linepart, " " );
            vec3[2] = atof( linepart.c_str() );

            if ( line != "" )
            {
                throw XError( "Errors while calling %s : %s :\n\tThe normal info in OBJ file has more than 3 elements.\n",
                               __FILE__, __FUNCTION__ );
            }
            
            // because the relation between vertices and normals is stored at
            // the face information of OBJ file, so the normals have to be
            // saved in a temporary array
            tmpnormals.push_back( vec3 );
        }

        // facet of mesh
        if( linepart == "f" )
        {
            // assumed it's triangles mesh
            std::string linepartpart; 
            unsigned int posindex;
            unsigned int texindex;
            unsigned int nmlindex;

            for ( unsigned int i=0; i<3; i++ )
            {
                // vertex index
                findNextPartString( &line, &linepart, " " );
                findNextPartString( &linepart, &linepartpart, "/" );
                posindex = atoi( linepartpart.c_str() ) - 1;
                bonesgeom->editAllPrimitiveSets()->front().editArray()->push_back( posindex );

                // texture index
                findNextPartString( &linepart, &linepartpart, "/" );
                if ( linepartpart != "" )
                {
                    if ( pos2tex.size() < pntoffset )
                        pos2tex.resize( pntoffset );

                    texindex = atoi( linepartpart.c_str() ) - 1;
                    pos2tex[ posindex ] = texindex;
                }

                // normal index
                findNextPartString( &linepart, &linepartpart, "/" );
                if ( linepartpart != "" )
                {
                    if ( pos2nml.size() < pntoffset )
                        pos2nml.resize( pntoffset );

                    nmlindex = atoi( linepartpart.c_str() ) - 1;
                    pos2nml[ posindex ] = nmlindex;
                } 
            }

            //findNextPartString( &line, &linepart, " " );
            //findNextPartString( &linepart, &linepartpart, "/" );
            //bonesgeom->editAllPrimitiveSets()->front().editArray()->push_back( atoi( linepartpart.c_str() ) -1 );
        }
    }
    limbsizes[ lastlimbid ] = pntoffset - lastpntoffset;

    ////////////////////////////////////////////////////////////////////////////
    // Processing the normals and texture
    //
    bonesgeom->hasNormals( true );
    for ( unsigned int i=0; i<pos2nml.size(); i++ )
    {
        bonesgeom->editNormals()->editArray()->push_back( tmpnormals[ pos2nml[i] ] );
    }

    ////////////////////////////////////////////////////////////////////////////
    // Adding the local data of limb's geometry reference to armature
    //
    for ( unsigned int i=0; i<num_limbs; i++ )
    {
        //armature->_alllimbrefs[i].setBonesOffset( limboffsets[i] );
        //armature->_alllimbrefs[i].setBonesSize  ( limbsizes  [i] );
        (*armature->editAllLimbRef())[i].editBonesRef()->addRange( limboffsets[i], limbsizes[i] );
    }

    ////////////////////////////////////////////////////////////////////////////
    // Debug info
    //
    printf( "all limbrefs----------------------------------------------\n" );
    for ( unsigned int i=0; i < armature->_alllimbrefs.size(); i++ )
    {
        printf( "id: %2d, name: %8s, offset: %6d, size: %6d\n",
                i, 
                (armature->_alllimbrefs)[i].getName().c_str(),
                (*armature->getAllLimbRef())[i].getBonesRef()->getArray()->front().first/*(armature->_alllimbrefs)[i].getBonesOffset()*/,
                (*armature->getAllLimbRef())[i].getBonesRef()->getArray()->front().second/*(armature->_alllimbrefs)[i].getBonesSize()*/ );
    }

    return;
}


/** 
 * 
 * @param skingeom
 * @param filename
 */
void Loader::loadSkinGeom( Geometry *skingeom,
                           const std::string &filefullname )
{
    skingeom->setFillMode( GL_FILL );

    ////////////////////////////////////////////////////////////////////////////
    // Assumed that the mesh is make of only triangles
    //
    skingeom->editAllPrimitiveSets()->resize( 1 );
    skingeom->editAllPrimitiveSets()->front().setType( GL_TRIANGLES );

    std::ifstream ifile( filefullname.c_str() );
    if ( ! ifile.is_open() )
    {
        throw XError( "Errors while calling %s : %s :\n\tCannot open file %s.\n",
                      __FILE__, __FUNCTION__,
                      filefullname.c_str() );
    }

    ////////////////////////////////////////////////////////////////////////////
    // Assign the geometry while reading the obj file
    //
    std::string line;
    std::string linepart;
    unsigned int pntoffset = 0;

    std::vector<Vector3> tmpnormals;
    std::vector<Point2f> tmptexture;
    std::vector<int> pos2nml;
    std::vector<int> pos2tex;

    while( ! ifile.eof() )
    {
        getline( ifile, line );
        findNextPartString( &line, &linepart, " " );

        // material
        if ( linepart == "mtllib" )
        {
            findNextPartString( &line, &linepart, " " );
            loadMaterial( skingeom, linepart );
        }

        // position of vertex
        if( linepart == "v" )
        {
            pntoffset += 1;

            Point3 pnt3;
            findNextPartString( &line, &linepart, " " );
            pnt3[0] = atof( linepart.c_str() );
            findNextPartString( &line, &linepart, " " );
            pnt3[1] = atof( linepart.c_str() );
            findNextPartString( &line, &linepart, " " );
            pnt3[2] = atof( linepart.c_str() );
            
            if ( line != "" )
            {
                throw XError( "Errors while calling %s : %s :\n\tThe vertex info in OBJ file has more than 3 elements.\n",
                               __FILE__, __FUNCTION__ );
            }

            skingeom->editPositions()->editArray()->push_back( pnt3 );
        }

        // normal of vertex
        if( linepart == "vn" )
        {
            Vector3 vec3;
            findNextPartString( &line, &linepart, " " );
            vec3[0] = atof( linepart.c_str() );
            findNextPartString( &line, &linepart, " " );
            vec3[1] = atof( linepart.c_str() );
            findNextPartString( &line, &linepart, " " );
            vec3[2] = atof( linepart.c_str() );

            if ( line != "" )
            {
                throw XError( "Errors while calling %s : %s :\n\tThe normal info in OBJ file has more than 3 elements.\n",
                               __FILE__, __FUNCTION__ );
            }
            
            // because the relation between vertices and normals is stored at
            // the face information of OBJ file, so the normals have to be
            // saved in a temporary array
            tmpnormals.push_back( vec3 );
        }

        // texture coordinate of vertex
        if( linepart == "vt" )
        {
            Point2f pnt2;
            findNextPartString( &line, &linepart, " " );
            pnt2.mX = atof( linepart.c_str() );
            findNextPartString( &line, &linepart, " " );
            pnt2.mY = atof( linepart.c_str() );

            if ( line != "" )
            {
                throw XError( "Errors while calling %s : %s :\n\tThe texture2D info in OBJ file has more than 2 elements.\n",
                               __FILE__, __FUNCTION__ );
            }
            
            // because the relation between vertices and normals is stored at
            // the face information of OBJ file, so the normals have to be
            // saved in a temporary array
            tmptexture.push_back( pnt2 );
        }

        // facet of mesh
        if( linepart == "f" )
        {
            // assumed it's triangles mesh
            std::string linepartpart; 
            unsigned int posindex;
            unsigned int texindex;
            unsigned int nmlindex;

            for ( unsigned int i=0; i<3; i++ )
            {
                findNextPartString( &line, &linepart, " " );

                // vertex index
                findNextPartString( &linepart, &linepartpart, "/" );
                posindex = atoi( linepartpart.c_str() ) - 1;
                skingeom->editAllPrimitiveSets()->front().editArray()->push_back( posindex );

                // texture index
                findNextPartString( &linepart, &linepartpart, "/" );
                if ( linepartpart != "" )
                {
                    if ( pos2tex.size() < pntoffset )
                        pos2tex.resize( pntoffset );

                    texindex = atoi( linepartpart.c_str() ) - 1;
                    pos2tex[ posindex ] = texindex;
                }

                // normal index
                findNextPartString( &linepart, &linepartpart, "/" );
                if ( linepartpart != "" )
                {
                    if ( pos2nml.size() < pntoffset )
                        pos2nml.resize( pntoffset );

                    nmlindex = atoi( linepartpart.c_str() ) - 1;
                    pos2nml[ posindex ] = nmlindex;
                } 
            }
        }
    }

    ////////////////////////////////////////////////////////////////////////////
    // Processing the normals and texture
    //
    skingeom->hasNormals( true );
    for ( unsigned int i=0; i<pos2nml.size(); i++ )
    {
        skingeom->editNormals()->editArray()->push_back( tmpnormals[ pos2nml[i] ] );
    }

    for ( unsigned int i=0; i<pos2tex.size(); i++ )
    {
        skingeom->editTexture()->editArray()->push_back( tmptexture[ pos2tex[i] ] );
    }

    ////////////////////////////////////////////////////////////////////////////
    // Debug info
    //

    return;
}


/** 
 * 
 * @param vhand
 * @param spherefile
 *
 * @pre. cond. Bones and Skin must be loaded before.
 *
 */
void Loader::loadSpheres( SphereFilledBody *spherebody, const std::string &filefullname )
{
    std::string line;
    std::string linepart;
    float shortest = std::numeric_limits<float>::max();

    std::ifstream ifile( filefullname.c_str() );
    if ( ! ifile.is_open() )
    {
        throw XError( "Errors while calling %s : %s :\n\tCannot open file %s.\n",
                      __FILE__, __FUNCTION__,
                      filefullname.c_str() );
    }

    while( ! ifile.eof() )
    {
        getline( ifile, line );
        if ( line == "" ) continue;
        findNextPartString( &line, &linepart, " " );
        float xc = atof( linepart.c_str() );
        findNextPartString( &line, &linepart, " " );
        float yc = atof( linepart.c_str() );
        findNextPartString( &line, &linepart, " " );
        float zc = atof( linepart.c_str() );
        findNextPartString( &line, &linepart, " " );
        float radius = atof( linepart.c_str() );
        Sphere sphr = { xc, yc, zc, radius };
        spherebody->editAllSphere()->push_back( sphr );

        if ( radius < shortest ) shortest = radius;
    }

    //// set sphere material for debugging
    //spherebody->hasMaterial( true );
    //spherebody->editMaterial()->diffuse [0] = 0.9;
    //spherebody->editMaterial()->diffuse [2] = 0.1;
    //spherebody->editMaterial()->specular[0] = 0.3;
    //spherebody->editMaterial()->specular[2] = 0.03;

    // set shortest
    spherebody->setShortestRadius( shortest );

}


int Loader::loadTopology( SphereTopology *topo,
                          const std::string &filefullname )
{
    std::string line;
    std::string linepart;
    std::ifstream ifile( filefullname.c_str() );
    if ( ! ifile.is_open() )
    {
        return -1;
    }

    // load topology
    while( ! ifile.eof() )
    {
        getline( ifile, line );
        if ( line == "" ) break; /* why is it necessary to add this test */
        //if ( line[0] == '&' )
        //{
            //findNextPartString( &line, &linepart, " " );
            //while ( line != "" )
            //{
                //findNextPartString( &line, &linepart, " " );
                //unsigned int index = atoi( linepart.c_str() );
                //topo->editMassBones()->push_back( index );
            //}
 
        //}
        else
        {
            findNextPartString( &line, &linepart, " " );
            unsigned int out = atoi( linepart.c_str() );
            findNextPartString( &line, &linepart, " " );
            unsigned int in = atoi( linepart.c_str() );
            findNextPartString( &line, &linepart, " " );

            AdjacencyType adjtype;
            //
            if( linepart == "skin_skin" )
            {
                adjtype = SKIN_SKIN;
            }
            else if( linepart == "skin_sphere" )
            {
                adjtype = SKIN_SPHERE;
            }
            else if( linepart == "sphere_sphere" )
            {
                adjtype = SPHERE_SPHERE;
            }
            else if( linepart == "sphere_bones" )
            {
                adjtype = SPHERE_BONES;
            }
            //
            else if( linepart == "map_sphere_bones" )
            {
                adjtype = MAP_SPHERE_BONES;
            }
            else if( linepart == "map_skin_sphere" )
            {
                adjtype = MAP_SKIN_SPHERE;
            }
            else 
            {
                throw XError( "Errors while calling %s : %s :\n",
                        __FILE__, __FUNCTION__ );
            }

            topo->editAllAdjacency()->push_back( Adjacency( out, in, adjtype ) );
        }
    }

    return 0;
}


void Loader::writeTopology( const SphereTopology *topo,
                            const std::string &filefullname )
{
    std::ofstream ofile( filefullname.c_str() );
    if ( ! ofile.is_open() )
    {
        throw XError( "Errors while calling %s : %s :\n\tCannot open file %s.\n",
                      __FILE__, __FUNCTION__,
                      filefullname.c_str() );
    }

    //ofile << "&";
    //for ( unsigned int i=0; i<topo->getMassBones()->size(); i++ )
    //{
        //ofile << " " << (*topo->getMassBones())[i];
    //}
    //ofile << std::endl;

    const std::vector<Adjacency> *alladj = topo->getAllAdjacency();
    for ( unsigned int i=0; i<alladj->size(); i++ )
    {
        ofile << (*alladj)[i];
    }
}


/** 
 * @param geom
 * @param filename
 */
void Loader::loadTexture( Geometry *geom,
                          const std::string &filename )
{
#if TEXTURE_SUPPORTED == 1
    geom->hasTexture( true );

    std::string filefullname = _fileloaddir + filename;

    // before this call, DevIL runtime must be initialized
    geom->editTexture()->editHandle()->loadImage( filefullname.c_str() );
#endif // TEXTURE_SUPPORTED
}

/** 
 *
 * @param geom
 * @param filename
 */
void Loader::loadMaterial( Geometry *geom,
                           const std::string &filename )
{
    geom->hasMaterial( true );

    std::string filefullname = _fileloaddir + filename;
    std::ifstream ifile( filefullname.c_str() );
    if ( ! ifile.is_open() )
    {
        throw XError( "Errors while calling %s : %s :\n\tCannot open file %s.\n",
                      __FILE__, __FUNCTION__,
                      filefullname.c_str() );
    }

    std::string line;
    std::string linepart;
    unsigned int pntoffset = 0;
    unsigned int lastpntoffset = 0;
    int lastlimbid = -1;

    while( ! ifile.eof() )
    {
        getline( ifile, line );
        findNextPartString( &line, &linepart, " " );

        // load texture
        if ( linepart == "map_Kd" )
        {
            findNextPartString( &line, &linepart, " " );
            loadTexture( geom, linepart );
        }
        // shininess
        if ( linepart == "Ns" )
        {
            findNextPartString( &line, &linepart, " " );
            geom->editMaterial()->shininess = atof( linepart.c_str() );
        }
        // ambient
        if ( linepart == "Ka" )
        {
            findNextPartString( &line, &linepart, " " );
            geom->editMaterial()->ambient[0] = atof( linepart.c_str() );
            findNextPartString( &line, &linepart, " " );
            geom->editMaterial()->ambient[1] = atof( linepart.c_str() );
            findNextPartString( &line, &linepart, " " );
            geom->editMaterial()->ambient[2] = atof( linepart.c_str() );
        }
        // diffuse
        if ( linepart == "Kd" )
        {
            findNextPartString( &line, &linepart, " " );
            geom->editMaterial()->diffuse[0] = atof( linepart.c_str() );
            findNextPartString( &line, &linepart, " " );
            geom->editMaterial()->diffuse[1] = atof( linepart.c_str() );
            findNextPartString( &line, &linepart, " " );
            geom->editMaterial()->diffuse[2] = atof( linepart.c_str() );
        }
        // specular
        if ( linepart == "Ks" )
        {
            findNextPartString( &line, &linepart, " " );
            geom->editMaterial()->specular[0] = atof( linepart.c_str() );
            findNextPartString( &line, &linepart, " " );
            geom->editMaterial()->specular[1] = atof( linepart.c_str() );
            findNextPartString( &line, &linepart, " " );
            geom->editMaterial()->specular[2] = atof( linepart.c_str() );
        }
    }
}


void Loader::loadSssArgument( SphereFilledBody *vh )
{
    float dt = atof( _config[ "increment" ].c_str() );
    vh->setDeltaTime( dt );

    unsigned int sn = atoi( _config[ "steps" ].c_str() );
    vh->setStepNum( sn );

    // skin-skin
    vh->setSpringArg( SKIN_SKIN,
                      atof( _config[ "skin-spring_ks" ].c_str() ),
                      atof( _config[ "skin-spring_kd" ].c_str() ) );

    // skin-spheres
    vh->setSpringArg( SKIN_SPHERE,
                      atof( _config[ "interspring_skinSphere_ks" ].c_str() ),
                      atof( _config[ "interspring_skinSphere_kd" ].c_str() ) );

    // spheres-spheres
    vh->setSpringArg( SPHERE_SPHERE,
                      atof( _config[ "spheres-spring_ks" ].c_str() ),
                      atof( _config[ "spheres-spring_kd" ].c_str() ),
                      atof( _config[ "spheres-spring_kv" ].c_str() ) );

    // spheres-bones
    vh->setSpringArg( SPHERE_BONES,
                      atof( _config[ "interspring_sphereBone_ks" ].c_str() ),
                      atof( _config[ "interspring_sphereBone_kd" ].c_str() ) );

    return;
}


} //namespace vhand
